package com.tuzhi.convert.pknuzip;


import cn.hutool.core.util.ZipUtil;
import net.sf.sevenzipjbinding.*;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import org.apache.commons.collections.map.FixedSizeMap;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.LongFunction;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;


/**
 * 文件解压缩工具类
 */
public class UnPagFileUtil {

    private static final Logger log = LoggerFactory.getLogger(UnPagFileUtil.class);

    /**
     * rar解压缩
     *
     * @param rarFile rar文件的全路径
     * @param outPath 解压路径
     * @return 压缩包中所有的文件
     */
    public static File unRarZip7Z(String rarFile, String outPath) throws Exception {
        if (rarFile.toLowerCase().endsWith(".zip")) {
            return ZipUtil.unzip(rarFile, outPath);
        } else if (rarFile.toLowerCase().endsWith(".rar")) {
            return unRar(rarFile, outPath, "");
        } else if (rarFile.toLowerCase().endsWith(".7z")) {
            return un7z2File(rarFile, outPath);
        }
        return null;
    }

    private static File unRar(String rarFile, String outPath, String passWord) throws Exception {
        try (
                //第一个参数是需要解压的压缩包路径，第二个参数参考JdkAPI文档的RandomAccessFile
                RandomAccessFile randomAccessFile = new RandomAccessFile(rarFile, "r");
                IInArchive inArchive = StringUtils.isNotBlank(passWord) ? SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile), passWord)
                        : SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile))
        ) {
            ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
            for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                final int[] hash = new int[]{0};
                if (!item.isFolder()) {
                    ExtractOperationResult result;
                    final long[] sizeArray = new long[1];

                    File outFile = new File(outPath + File.separator + item.getPath());
                    File parent = outFile.getParentFile();
                    if ((!parent.exists()) && (!parent.mkdirs())) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(passWord)) {
                        result = item.extractSlow(data -> {
                            try (FileOutputStream fileOutputStream = new FileOutputStream(outFile, true);) {
                                IOUtils.write(data, fileOutputStream);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed
                        }, passWord);
                    } else {
                        result = item.extractSlow(data -> {
                            try (FileOutputStream fileOutputStream = new FileOutputStream(outFile, true);) {
                                IOUtils.write(data, fileOutputStream);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed
                        });
                    }

                    if (result == ExtractOperationResult.OK) {
                        log.info("解压rar成功...." + String.format("%9X | %10s | %s", hash[0], sizeArray[0], item.getPath()));
                    } else if (StringUtils.isNotBlank(passWord)) {
                        log.info("解压rar成功：密码错误或者其他错误...." + result);
//                        return "password";
                        throw new Exception("解压rar成功：密码错误或者其他错误...." + result);
                    } else {
//                        return "rar error";
                        throw new Exception("rar error");
                    }
                }

            }
        } catch (Exception e) {
            log.error("unRar error", e);
            throw new Exception(e.getMessage());
        }
        return new File(outPath);
    }

    /**
     * 7z解压缩
     *
     * @param z7zFilePath 7z文件的全路径
     * @param outPath     解压路径
     * @return 压缩包中所有的文件
     */
    public static String un7z2Path(String z7zFilePath, String outPath) {

        SevenZFile zIn = null;
        try {
            File file = new File(z7zFilePath);
            zIn = new SevenZFile(file);
            SevenZArchiveEntry entry = null;
            File newFile = null;
            while ((entry = zIn.getNextEntry()) != null) {
                //不是文件夹就进行解压
                if (!entry.isDirectory()) {
                    newFile = new File(outPath, entry.getName());
                    if (!newFile.exists()) {
                        new File(newFile.getParent()).mkdirs();   //创建此文件的上层目录
                    }
                    OutputStream out = new FileOutputStream(newFile);
                    BufferedOutputStream bos = new BufferedOutputStream(out);
                    int len = -1;
                    byte[] buf = new byte[(int) entry.getSize()];
                    while ((len = zIn.read(buf)) != -1) {
                        bos.write(buf, 0, len);
                    }
                    bos.flush();
                    bos.close();
                    out.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (zIn != null) {
                    zIn.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return outPath;
    }

    /**
     * 7z解压缩
     *
     * @param z7zFilePath 7z文件的全路径
     * @param outPath     解压路径
     * @return 压缩包中所有的文件
     */
    public static Map<String, String> un7z2FileList(String z7zFilePath, String outPath) {
        String out = un7z2Path(z7zFilePath, outPath);
        Map<String, String> resultMap = getFileNameList(out, "");
        return resultMap;
    }

    /**
     * 7z解压缩
     *
     * @param z7zFilePath 7z文件的全路径
     * @param outPath     解压路径
     * @return 压缩包中所有的文件
     */
    public static File un7z2File(String z7zFilePath, String outPath) {
        String out = un7z2Path(z7zFilePath, outPath);
        return new File(out);
    }


    /**
     * 获取压缩包中的全部文件
     *
     * @param path 文件夹路径
     * @return 压缩包中所有的文件
     * @childName 每一个文件的每一层的路径==D==区分层数
     */
    private static Map<String, String> getFileNameList(String path, String childName) {
        System.out.println("path:" + path + "---childName:" + childName);
        Map<String, String> files = new HashMap<>();
        File file = new File(path); // 需要获取的文件的路径
        String[] fileNameLists = file.list(); // 存储文件名的String数组
        File[] filePathLists = file.listFiles(); // 存储文件路径的String数组
        for (int i = 0; i < filePathLists.length; i++) {
            if (filePathLists[i].isFile()) {
                files.put(fileNameLists[i] + "==D==" + childName, path + File.separator + filePathLists[i].getName());
            } else {
                files.putAll(getFileNameList(path + File.separator + filePathLists[i].getName(), childName + "&" + filePathLists[i].getName()));
            }
        }
        return files;
    }


    public static void main(String[] args) {
        try {
           /* String inputFile = "E:\\新建文件夹 (2)\\压缩测试.zip";
            String inputFile2 = "E:\\新建文件夹 (2)\\红光村批复.rar";
            String inputFile3 = "E:\\新建文件夹 (2)\\压缩测试.7z";
            String destDirPath = "E:\\新建文件夹\\zip";
            String destDirPath2 = "E:\\新建文件夹\\rar";
            String destDirPath3 = "E:\\新建文件夹\\7z";
            String suffix = inputFile.substring(inputFile.lastIndexOf("."));
            //File zipFile = new File("E:\\新建文件夹 (2)\\压缩测试.7z");
            //un7z(inputFile3, destDirPath3);
            //unZip(inputFile, destDirPath, false);
            unRar(inputFile2, destDirPath2, "");*/
//            File file = unRar("E:\\tz-java-group-3\\tz-converter-java\\cadFiles\\AL00994-PP-convert.rar", "E:\\convert\\unziprar\\test12", null);
//            System.out.println(file);
            File file = ZipUtil.unzip("E:\\tz-java-group-3\\tz-converter-java\\cadFiles\\AL00994-PP.zip",
                    "E:\\convert\\unziprar");
            System.out.println(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}